package inteligenciaartificial;

import inteligenciaartificial.Individuo.Operador;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.Map;

public class GeneracionManager {
    
	private static ArrayList<Generacion> generaciones;
        public static int cantidadGeneraciones;
        public static double porcentajeDeAceptacion;
        
        private static Map <Individuo.Operador,Integer> algoritmos= new EnumMap<Individuo.Operador,Integer>(Individuo.Operador.class); 
        private static int numeroDeUltimaGeneracion=0;
        
        public static void inicializar(){
            cantidadGeneraciones= Settings.cantidadGeneraciones;
            porcentajeDeAceptacion= Settings.porcentajeDeAceptacion;
            algoritmos= Settings.algoritmosGeneticos;
        }
        
        public static Generacion crearGeneracionInicial(){
            
            GeneracionManager.inicializar();
            
            generaciones= new ArrayList<Generacion>();
            
            Individuo[] individuos= new Individuo[Settings.totalPoblacion];
            
            for(int i=0;i<individuos.length;i++){
                Individuo individuoAleatorio= IndividuoManager.crearIndividuoAleatorio();
                individuoAleatorio.setNroGeneracion(0);
                individuoAleatorio.setNroIndividuo(i);
                individuos[i]=individuoAleatorio;         
            }
            
            Generacion generacionInicial = new Generacion(individuos);
            GeneracionManager.generaciones.add(generacionInicial);
            return generacionInicial;
        }
        
        public static Generacion crearNuevaGeneracion(){

            int cantSeleccionados=0;
            int cantCruzados=0;
            int cantMutados=0;
            
            Operador[] operadores= Individuo.Operador.values();
            
            //recorro el arreglo de operadores, me fijo si estan en el Map y actualizo las cantidades de individuos a saleccionar, cruzar y mutar
            for(int i=0;i<operadores.length;i++){
                Operador operador=operadores[i];
                
                if( operador==Operador.SeleccionElitista            ||
                    operador==Operador.SeleccionPorCopiasEsperadas  ||
                    operador==Operador.SeleccionPorRanking          ||
                    operador==Operador.SeleccionPorRuleta           ||
                    operador==Operador.SeleccionPorTorneo){
                    if(algoritmos.containsKey(operador))
                    cantSeleccionados+= algoritmos.get(operador) * Settings.totalPoblacion /100;
                }
                
                if( operador==Operador.CruzaSimple      ||
                    operador==Operador.CruzaMultipunto  ||
                    operador==Operador.CruzaBinomial){
                    if(algoritmos.containsKey(operador))
                    cantCruzados+= algoritmos.get(operador) * Settings.totalPoblacion /100;
                }
                
                if( operador==Operador.MutacionPorTempAscendente ||
                    operador==Operador.MutacionPorTempDescendente ||
                    operador==Operador.Mutacion   ){
                    if(algoritmos.containsKey(operador))
                    cantMutados+= algoritmos.get(operador) * Settings.totalPoblacion /100;
                }
            }
    
            Individuo[] seleccionados= new Individuo[cantSeleccionados];
            Individuo[] cruzados=new Individuo[cantCruzados];
            Individuo[] mutados=new Individuo[cantMutados];
            
            for(int i=0;i<operadores.length;i++){
                Operador operador=operadores[i];
                 if(algoritmos.containsKey(operador)){
                     int cantidadDeIndividuos= algoritmos.get(operador)* Settings.totalPoblacion /100;
                     if(operador==Operador.SeleccionElitista){
                        Individuo[] sel = Selector.seleccionElitista(generaciones.get(numeroDeUltimaGeneracion).getIndividuos(), cantidadDeIndividuos);
                        seleccionados= GeneracionManager.agregarIndividuos(seleccionados, sel);
                     }
                     if(operador==Operador.SeleccionPorRanking){
                         Individuo[] sel = Selector.seleccionarRanking(generaciones.get(numeroDeUltimaGeneracion).getIndividuos(), cantidadDeIndividuos);
                         seleccionados= GeneracionManager.agregarIndividuos(seleccionados, sel);
                     }
                     if(operador==Operador.SeleccionPorCopiasEsperadas){
                         Individuo[] sel = Selector.controlCopiasEsperadas(generaciones.get(numeroDeUltimaGeneracion).getIndividuos(), cantidadDeIndividuos);
                         seleccionados= GeneracionManager.agregarIndividuos(seleccionados, sel);
                     }
                     if(operador==Operador.SeleccionPorRuleta){
                         Individuo[] sel = Selector.seleccionarRuleta(generaciones.get(numeroDeUltimaGeneracion).getIndividuos(), cantidadDeIndividuos);
                         seleccionados= GeneracionManager.agregarIndividuos(seleccionados, sel);
                     }
                     if(operador==Operador.SeleccionPorTorneo){
                         Individuo[] sel = Selector.seleccionarPorTorneo(generaciones.get(numeroDeUltimaGeneracion).getIndividuos(), cantidadDeIndividuos);
                         seleccionados= GeneracionManager.agregarIndividuos(seleccionados, sel);
                     }
                     if(operador==Operador.CruzaSimple){
                         Individuo[] cru = Cruzador.cruzaSimple(generaciones.get(numeroDeUltimaGeneracion).getIndividuos(), cantidadDeIndividuos);
                         cruzados= GeneracionManager.agregarIndividuos(cruzados, cru);
                     }
                     if(operador==Operador.CruzaMultipunto){
                         Individuo[] cru = Cruzador.cruzaMultipunto(generaciones.get(numeroDeUltimaGeneracion).getIndividuos(),cantidadDeIndividuos);
                         cruzados= GeneracionManager.agregarIndividuos(cruzados, cru);
                     }
                     if(operador==Operador.CruzaBinomial){
                         Individuo[] cru = Cruzador.cruzaBinomial(generaciones.get(numeroDeUltimaGeneracion).getIndividuos(), cantidadDeIndividuos);
                         cruzados= GeneracionManager.agregarIndividuos(cruzados, cru);   
                     }
                     if(operador==Operador.MutacionPorTempAscendente || operador==Operador.Mutacion){
                         Individuo[] mut = Mutador.mutarPorTemperaturaAscendente(generaciones.get(numeroDeUltimaGeneracion), cantidadDeIndividuos);
                         mutados= GeneracionManager.agregarIndividuos(mutados, mut);  
                     }
                     if(operador==Operador.MutacionPorTempDescendente){
                         Individuo[] mut = Mutador.mutarPorTemperaturaAscendente(generaciones.get(numeroDeUltimaGeneracion), cantidadDeIndividuos);
                         mutados= GeneracionManager.agregarIndividuos(mutados, mut); 
                     }
                 }     
            }
                     
            Generacion nuevaGeneracion= new Generacion(seleccionados,cruzados, mutados, numeroDeUltimaGeneracion+1);
            
            generaciones.add(nuevaGeneracion);
            
            numeroDeUltimaGeneracion++;
            
            return nuevaGeneracion;
        }
     
        public static Generacion getGeneracionNumero(int indice){
            return generaciones.get(indice);
        }
        
        public static ArrayList<Generacion> getGeneraciones(){
            return  generaciones;
        }
        
        public static void agregarAlgoritmoGenetico(Individuo.Operador operador,Integer cantidad){
            algoritmos.put(operador, cantidad);
        }
        
        private static Individuo[] agregarIndividuos(Individuo[] destino, Individuo[] fuente){
            Individuo[] agregados= new Individuo[destino.length];
            int i=0;
            while(destino[i]!=null && i<destino.length){
                agregados[i]=destino[i];
                i++;
            }
            for(int j=0; j<fuente.length;j++){
                agregados[j+i]=fuente[j];
            }
            
            return agregados;
        }
        
        public static void setAlgoritmos(Map <Individuo.Operador,Integer> algoritmosGeneticos){
            algoritmos= algoritmosGeneticos;
        }

    static void borrarTodo() {
        if(generaciones!=null){
            generaciones.clear();
            numeroDeUltimaGeneracion=0;
        }
    }
        
}
